package com.today.cjl.sqlGen.utils

import java.io.PrintWriter

import scala.annotation.tailrec

/**
  * Created by cjl at 2018-09-03
  * Description: 
  * Changed: 
  */
trait GenUtil[T] extends CommonVar {

  def gen(argument: T): Unit

  /**
    * 生成sql   参数列表  +  模板 => out.sql
    * @param argsList
    */
  protected def genSql(argsList: List[List[Map[String, Any]]]): Unit = {
    val zipList: List[(String, List[Map[String, Any]])] = genTemps() zip argsList

    val res: List[String] = zipList.flatMap { case (temp, args) =>
      args.map(
        genSqlFromTemp(temp, _)
      ) :+ ""
    }
    FileUtil.writeLine(OUT_FILE)(res)

  }

  /**
    * 生成sql模板
    * @return
    */
  protected def genTemps(): List[String] = {
    val lines: List[String] = FileUtil.readLine(TEMP_FILE)
    lineSeq(lines).map { ls: List[String] =>
      val sqlTemp = ls.reduce(_ + " " + _)
      if (!sqlTemp.endsWith(";")) sqlTemp + ";" else sqlTemp
    }
  }

  /**
    * 行数据分割 NEW_LINE_SEP => 换一个新组   NULL_LINE_SEP => 占一个空组
    * @param lines
    * @return
    */
  protected def lineSeq(lines: List[String]): List[List[String]] = {
    @tailrec
    def seq(p: String => Boolean)(res: List[List[String]], list: List[String]): List[List[String]] = {
      list match {
        case Nil => res
        case head::tail =>
          // p:  != NEW_LINE_SEP
          val (left, right) = if (p(head)) list.span(p) else tail.span(p)  // 去掉 换行标示符
          val r = left match {
            case Nil => res
            case li if li.contains(NULL_LINE_SEP) => res :+ Nil   // null 占据一个元素
            case _ => res :+ left
          }
          seq(p)(r, right)
      }
    }

    //是否启用换行
    if (NEW_LINE_SEP.nonEmpty) {
      seq(!_.startsWith(NEW_LINE_SEP))(Nil, lines)
    } else {
      List(lines)
    }
  }

  /**
    * sql模板替换变量 => sql
    * @param temp
    * @param args
    * @return
    */
  def genSqlFromTemp(temp: String, args: Map[String, Any]): String = {

    @tailrec
    def replace(temp: String, args: List[(String, Any)]): String = {
      args match {
        case Nil => temp
        case (argName: String, argVal: Any)::tail =>
          val str: String = argVal match {
            case s if s.isInstanceOf[String] => s"'$argVal'"
            case _ => argVal.toString
          }
          val r: String = temp.replaceAll(s"@$argName", str)
          replace(r, tail)
      }
    }

    replace(temp, args.toList)
  }
}
